package scu.maqiang.mesh;

import java.io.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
import java.util.stream.IntStream;

import scu.maqiang.numeric.*;

public class Mesh2T6 extends Mesh {

	public Mesh2T6() {
		super();
		nDim = 2;
		nPerElement = 6;
		nPerBoundary = 3;
		nBoundaryPerElement = 3;
		nEdgePerElement = 6;
		nEdgePerBoundary = 3;
		tecplotType = "FETRIANGLE";
	}

	public Mesh2T6 square(int xNum, int yNum, T3Type tp) {
		int i, j, li, lj, lk, nv, nt, nb, xNodes, yNodes;
		int[] t = new int[9];
		xNodes = 2 * xNum + 1;
		yNodes = 2 * yNum + 1;
		nv = xNodes * yNodes;
		nt = 2 * xNum * yNum;
		nb = 2 * (xNum + yNum);
		initMesh0(nv, nt, nb);
		int count = 0;
		for (j = 0; j < yNodes; j++) {
			for (i = 0; i < xNodes; i++) {
				nodes[count++] = new double[] {0.5 * i / xNum, 0.5 * j / yNum};
			}
		}

		count = 0;
		switch (tp) {
			case Left:
				for (j = 0; j < yNum; j++) {
					for (i = 0; i < xNum; i++) {
						t[0] = 2 * (i + j * xNodes);
						t[1] = t[0] + 1;
						t[2] = t[1] + 1;
						t[3] = t[0] + xNodes;
						t[4] = t[3] + 1;
						t[5] = t[4] + 1;
						t[6] = t[3] + xNodes;
						t[7] = t[6] + 1;
						t[8] = t[7] + 1;
						elements[count++] = new int[] {t[0], t[2], t[8], t[1], t[5], t[4]};
						elements[count++] = new int[] {t[0], t[8], t[6], t[4], t[7], t[3]};
					}
				}
				break;
			case Right:
				for (j = 0; j < yNum; j++) {
					for (i = 0; i < xNum; i++) {
						t[0] = 2 * (i + j * xNodes);
						t[1] = t[0] + 1;
						t[2] = t[1] + 1;
						t[3] = t[0] + xNodes;
						t[4] = t[3] + 1;
						t[5] = t[4] + 1;
						t[6] = t[3] + xNodes;
						t[7] = t[6] + 1;
						t[8] = t[7] + 1;
						elements[count++] = new int[] {t[0], t[2], t[6], t[1], t[4], t[3]};
						elements[count++] = new int[] {t[2], t[8], t[6], t[5], t[7], t[4]};
					}
				}
				break;
			case Cross1:
				for (j = 0; j < yNum; j++) {
					for (i = 0; i < xNum; i++) {
						t[0] = 2 * (i + j * xNodes);
						t[1] = t[0] + 1;
						t[2] = t[1] + 1;
						t[3] = t[0] + xNodes;
						t[4] = t[3] + 1;
						t[5] = t[4] + 1;
						t[6] = t[3] + xNodes;
						t[7] = t[6] + 1;
						t[8] = t[7] + 1;
						if ((i + j) % 2 == 0) {
							elements[count++] = new int[] {t[0], t[2], t[8], t[1], t[5], t[4]};
							elements[count++] = new int[] {t[0], t[8], t[6], t[4], t[7], t[3]};
						} else {
							elements[count++] = new int[] {t[0], t[2], t[6], t[1], t[4], t[3]};
							elements[count++] = new int[] {t[2], t[8], t[6], t[5], t[7], t[4]};
						}
					}
				}
				break;
			case Cross2:
				for (j = 0; j < yNum; j++) {
					for (i = 0; i < xNum; i++) {
						t[0] = 2 * (i + j * xNodes);
						t[1] = t[0] + 1;
						t[2] = t[1] + 1;
						t[3] = t[0] + xNodes;
						t[4] = t[3] + 1;
						t[5] = t[4] + 1;
						t[6] = t[3] + xNodes;
						t[7] = t[6] + 1;
						t[8] = t[7] + 1;
						if ((i + j) % 2 == 0) {
							elements[count++] = new int[] {t[0], t[2], t[6], t[1], t[4], t[3]};
							elements[count++] = new int[] {t[2], t[8], t[6], t[5], t[7], t[4]};

						} else {
							elements[count++] = new int[] {t[0], t[2], t[8], t[1], t[5], t[4]};
							elements[count++] = new int[] {t[0], t[8], t[6], t[4], t[7], t[3]};
						}
					}
				}
				break;
			default:
				break;
		}

		count = 0;
		for (i = 0; i < xNum; i++) {
			li = 2 * i;
			lj = li + 2;
			lk = li + 1;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++] = 1;
		}

		for (i = 0; i < xNum; i++) {
			li = yNodes * xNodes - 1 - 2 * i;
			lj = li - 2;
			lk = li - 1;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++] = 3;
		}

		for (i = 0; i < yNum; i++) {
			li = (2 * i + 1) * xNodes - 1;
			lj = li + 2 * xNodes;
			lk = li + xNodes;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++] = 2;
		}

		for (i = 0; i < yNum; i++) {
			//li = (2 * yNum - 1 - 2 * i) * xNodes;
			//li = (2 * yNum   - 2 * (i - 1)) * xNodes + 1;
			li = 2 * yNum * xNodes - 2 * i * xNodes;
			lj = li - 2 * xNodes;
			lk = li - xNodes;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++] = 4;
		}
		return this;
	}

	public Mesh2T6(Mesh2T3 mesh0) {
		this();
		SIMatrix C = mesh0.renumberingEdges();
		int sizeC = C.getNNZ();
		int[] idxI = new int[sizeC];
		int[] idxJ = new int[sizeC];
		int[] value = new int[sizeC];
		int[] number = new int[sizeC];
		C.getTriValues(idxI, idxJ, value);
		for (int i = 0; i < sizeC; i++) {
			number[i] = i;
		}

		nv = sizeC + mesh0.nv;
		nodes = new double[nv][];
		nodeLabel = new int[nv];
		int[][] edges = new int[mesh0.nt][3];
		for (int i = 0; i < mesh0.nt; i++) {
			int[] tri = mesh0.elements[i];
			int d1 = tri[0];
			int d2 = tri[1];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][0] = j + mesh0.nv;
					break;
				}
			}
			d1 = tri[1];
			d2 = tri[2];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][1] = j + mesh0.nv;
					break;
				}
			}
			d1 = tri[2];
			d2 = tri[0];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][2] = j + mesh0.nv;
					break;
				}
			}

		}

		for (int i = 0; i < mesh0.nv; i++) {
			nodes[i] = mesh0.nodes[i].clone();
			nodeLabel[i] = mesh0.nodeLabel[i];
		}
		for (int i = mesh0.nv; i < nv; i++) {
			nodes[i] = new double[nDim];
		}
		nt = mesh0.nt;
		elements = new int[nt][];
		elementLabel = mesh0.elementLabel.clone();
		for (int i = 0; i < mesh0.nt; i++) {
			int[] tri = mesh0.elements[i];
			double[][] node = new double[3][];
			for (int j = 0; j < 3; j++) {
				node[j] = mesh0.nodes[tri[j]];
			}
			nodes[edges[i][0]] = new double[] {0.5 * (node[0][0] + node[1][0]), 0.5 * (node[0][1] + node[1][1])};
			nodes[edges[i][1]] = new double[] {0.5 * (node[1][0] + node[2][0]), 0.5 * (node[1][1] + node[2][1])};;
			nodes[edges[i][2]] = new double[] {0.5 * (node[2][0] + node[0][0]), 0.5 * (node[2][1] + node[0][1])};
			elements[i] = new int[] { tri[0], tri[1], tri[2], edges[i][0], edges[i][1], edges[i][2] };;
		}

		nb = mesh0.nb;
		boundaries = new int[nb][];
		boundaryLabel = mesh0.boundaryLabel.clone();
		int d1, d2;
		for (int i = 0; i < nb; i++) {
			int[] li = mesh0.boundaries[i];
			d1 = li[0];
			d2 = li[1];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					boundaries[i] = new int[] {d1, d2, j + mesh0.nv};
					break;
				}
			}
		}
	}

	public Mesh2T6 meshFromANSYS(String fileName) {
		File file = new File(fileName);
		try (Scanner sc = new Scanner(file)) {
			nv = sc.nextInt();
			nt = sc.nextInt();
			nodes = new double[nv][];
			nodeLabel = new int[nv];
			elements = new int[nt][];
			elementLabel = new int[nt];
			double x, y;
			for (int i = 0; i < nv; i++) {
				sc.nextInt();
				x = sc.nextDouble();
				y = sc.nextDouble();
				nodes[i] = new double[] {x, y};
			}

			int[] t = new int[8];
			for (int i = 0; i < nt; i++) {
				for (int j = 0; j < 8; j++) {
					t[j] = sc.nextInt();
				}
				elements[i] = new int[] {t[0], t[1], t[2], t[4], t[5], t[7]};
				elementLabel[i] = sc.nextInt();;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		HashSet<UnorderIntArray> set = extractBoundarysFromElements();
		setBoundaries(set);
		return this;
	}

	@Override
	public double[] getinteriorPointInElement(double[][] coord) {
		double[] x = new double[3];
		double[] y = new double[3];
		for(int i = 0; i < 3; i++) {
			x[i] = coord[i][0];
			y[i] = coord[i][1];
		}
		return new double[]{ MVO.average(x), MVO.average(y) };
	}


	@Override
	public boolean checkMesh(double[][] coord) {
		return CG.area2DT3(coord) > 0;
	}


	@Override
	public double[] getinteriorPointOnBoundary(double[][] coord) {
		return coord[2].clone();
	}

	@Override
	public int[][] getBoundarysFromElement(int i) {
		int[][] boundarys = new int[nBoundaryPerElement][];
		int[] ele = elements[i];
		boundarys[0] = new int[] {ele[0], ele[1], ele[3]};
		boundarys[1] = new int[] {ele[1], ele[2], ele[4]};
		boundarys[2] = new int[] {ele[2], ele[0], ele[5]};
		return boundarys;
	}
	
	@Override
	public void toTecplot(String fileName) {
		try (FileWriter fw = new FileWriter(fileName)){
			fw.write(Tecplot.meshFileString + "\n");
			fw.write(Tecplot.dimVariableString[nDim - 1] + "\n");

			fw.write(Tecplot.ZoneString(this) + "\n");
			for(int i = 0; i < nv; i++) {
				for(int j = 0; j < nDim; j++) {
					fw.write(nodes[i][j] + "\t");
				}
				fw.write("\n");
			}

			writeElements(fw);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void toTecplot(String fileName, double[] x) {
		try (FileWriter fw = new FileWriter(fileName)){
			fw.write("title = mesh file\n");
			fw.write("variables= \"x\", \"y\", \"u\"\n");

			fw.write(Tecplot.ZoneString(this) + "\n");
			
			for(int i = 0; i < nv; i++) {
				for(int j = 0; j < nDim; j++) {
					fw.write(nodes[i][j] + "\t");
				}
				fw.write(x[i] + "\n");
			}
			writeElements(fw);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void toTecplot(String fileName, double[][] mat) {
		int row = mat.length;
		try (FileWriter fw = new FileWriter(fileName)){
			fw.write("title = solution file\n");
			fw.write("variables= \"x\", \"y\"");
			for(int i = 0; i < row; i++) {
				fw.write(", \"u" + (i + 1) + "\"");
			}
			fw.write("\n");

			fw.write(Tecplot.ZoneString(this) + "\n");
			
			for(int i = 0; i < nv; i++) {
				for(int j = 0; j < nDim; j++) {
					fw.write(nodes[i][j] + "\t");
				}
				for(int j = 0; j < row; j++) {
					fw.write(mat[j][i] + "\t");
				}
				fw.write("\n");
			}


			writeElements(fw);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}

	@Override
	public void toTecplot(String fileName, double time, double[] x) {
		try (FileWriter fw = new FileWriter(fileName)){
			if (Math.abs(time) < Constants.Er) {
				fw.write("title = solution file\n");
				fw.write("variables= \"x\", \"y\", \"u\"\n");
				fw.write(Tecplot.ZoneStringTime(this) + time + "\n");
				for(int i = 0; i < nv; i++) {
					for(int j = 0; j < nDim; j++) {
						fw.write(nodes[i][j] + "\t");
					}
					fw.write(x[i] + "\n");
				}
				
				writeElements(fw);
				fw.flush();
				fw.close();
			} else {
				fw.write(Tecplot.ZoneStringShareTime(this) + time + "\n");
				for (double xx : x) {
					fw.write(xx + "\n");
				}
				fw.flush();
				fw.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void toTecplot(String fileName, double time, double[][] mat) {
		int row = mat.length;
		try (FileWriter fw = new FileWriter(fileName)){
			if (Math.abs(time) < Constants.Er) {
				fw.write("title = mesh file\n");
				fw.write("variables= \"x\", \"y\"");
				for(int j = 0; j < row; j++) {
					fw.write(", \"u" + (j + 1) + "\"");
				}
				fw.write("\n");				

				fw.write(Tecplot.ZoneString(this) + time + "\n");
				for(int i = 0; i < nv; i++) {
					for(int j = 0; j < nDim; j++) {
						fw.write(nodes[i][j] + "\t");
					}
					for(int j = 0; j < row; j++) {
						fw.write(mat[j][i] + "\t");
					}
					fw.write("\n");
				}
				

				writeElements(fw);
				fw.flush();
				fw.close();
			} else {
				fw.write(Tecplot.ZoneStringShareTime(this) + time + "\n");
				int col = mat[0].length;
				for(int i = 0; i < row; i++) {
					for(int j = 0; j < col; j++) {
						fw.write(mat[i][j] + "\t");
					}
					fw.write("\n");
				}
				fw.flush();
				fw.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	private void writeElements(FileWriter fw) throws IOException{
			for(int i = 0; i < nt; i++) {
				
			int[] idx = elements[i];
			fw.write((idx[0] + 1) + "\t" + (idx[3] + 1) + "\t" + (idx[5] + 1) + "\n");
			fw.write((idx[3] + 1) + "\t" + (idx[1] + 1) + "\t" + (idx[4] + 1) + "\n");
			fw.write((idx[5] + 1) + "\t" + (idx[4] + 1) + "\t" + (idx[2] + 1) + "\n");
			fw.write((idx[3] + 1) + "\t" + (idx[4] + 1) + "\t" + (idx[5] + 1) + "\n");
		}
	}

	@Override
	public int getTecplotNt() {
		return 2 * nt;
	}

	@Override
	public int[][] getEdgesFromElement(int i) {
		int[][] result = new int[6][];
		int[] ele = elements[i];
		result[0] = new int[] {ele[0], ele[3]};
		result[1] = new int[] {ele[3], ele[1]};
		result[2] = new int[] {ele[1], ele[4]};
		result[3] = new int[] {ele[4], ele[2]};
		result[4] = new int[] {ele[2], ele[5]};
		result[5] = new int[] {ele[5], ele[0]};
		return result;
	}
}
